#include "light.h"

/*******************************************************************************
 * Light properties
 * msdn.microsoft.com/en-us/library/windows/desktop/bb174696(v=vs.85).aspx
 * 
 * Light types
 * msdn.microsoft.com/en-us/library/windows/desktop/bb174697(v=vs.85).aspx
 *  
 * Type of color that applies most heavily to the system's computations is the 
 * diffuse color. The most common diffuse color is white.
 * 
 * Note that although Direct3D uses RGBA values for lights, the alpha color 
 * component is not used.
 ******************************************************************************/


CLight::CLight() 
{
    /**
     * Constructor. Ensure that we initialise and obtain enough memory for
     * the light in memory.
     */
    ZeroMemory(&m_Light, sizeof(D3DLIGHT9));
}

CLight::CLight(const D3DLIGHT9 &light):
    m_Light(light)
{
    /**
     * Copy constructor. Creates a new CLight with the passed in light as 
     * the light.
     */
}

void CLight::setType(D3DLIGHTTYPE nType) 
{
    /* 
     * Sets the light type to either one of the point spot or directional lights
     * 
     * Point light gives off equal light in all directions
     * Directional light emits parallel light in a given direction
     * Spot light emits a cone of light with a bright inner cone and a larger 
     * outer cone
     *
     * @param D3DLIGHTTYPE type The type of light that the light will be set to 
     *
     */
    m_Light.Type = nType;
}

D3DLIGHTTYPE CLight::getType() 
{
    /* 
     * @return D3DLIGHTTYPE The type that the light is currently 
     */
    return m_Light.Type;
}

void CLight::setPosition(float fX, float fY, float fZ) 
{
    /**
     * Creates and initialises a vector using the parameters and sets it as the 
     * lights new position.
     * Directional lights don't use the position property
     *
     * @param float x The x coordinate
     * @param float y The y coordinate
     * @param float z The z coordinate
     */
    D3DVECTOR arrPosition = {fX, fY, fZ};
    m_Light.Position = arrPosition;
}

void CLight::setPosition(const D3DVECTOR &arrPosition) 
{
   /**
     * Sets the new position of the light
     *
     * @param D3DVECTOR position The new position of the light.
     *
     */
    m_Light.Position = arrPosition;
}

void CLight::getPosition(float &fX, float &fY, float &fZ) const 
{
    /**
     * Obtains the position of the light.
     *
     * @param float &x A reference to the variable to hold the lights x 
     *  coordinate
     * @param float &y A reference to the variable to hold the lights y 
     *  coordinate
     * @param float &z A reference to the variable to hold the lights z 
     *  coordinate
     */
    fX = m_Light.Position.x;
    fY = m_Light.Position.y;
    fZ = m_Light.Position.z;
}

D3DVECTOR CLight::getPosition() const 
{
    /* 
     * @return D3DVECTOR The position that the light is currently at. 
     */
    return m_Light.Position;
}

void CLight::setDirection(float fX, float fY, float fZ) 
{
    /**
     * Creates and initialises a vector using the parameters and sets it as the 
     * lights new direction.
     * Only used by directional lights and spot lights
     *
     * @param float x The x coordinate
     * @param float y The y coordinate
     * @param float z The z coordinate
     *
     */
    D3DVECTOR arrDirection = {fX, fY, fZ};
    m_Light.Direction = arrDirection;
}

void CLight::setDirection(const D3DVECTOR &arrDirection) 
{
    /**
     * Sets the new direction of the light
     *
     * @param D3DVECTOR direction The new direction of the light.
     *
     */
    m_Light.Direction = arrDirection;
}

void CLight::getDirection(float &fX, float &fY, float &fZ) const 
{
    /**
     * Obtains the Direction of the light.
     *
     * @param float &x A reference to the variable to hold the lights x 
     *  direction coordinate
     * @param float &y A reference to the variable to hold the lights y 
     *  direction coordinate
     * @param float &z A reference to the variable to hold the lights z 
     *  direction coordinate
     */
   fX = m_Light.Direction.x;
   fY = m_Light.Direction.y;
   fZ = m_Light.Direction.z;
}

D3DVECTOR CLight::getDirection() const 
{
    /* 
     * @return D3DVECTOR The direction that the light is currently facing.
     */
    return m_Light.Direction;
}



void CLight::setDiffuse(float fR, float fG, float fB, float fA) 
{
    /**
     * Creates and initialises a D3DCOLORVALUE using the parameters and sets 
     * it as the lights new diffuse.
     *
     * The diffuse color reacts with the materials diffuse color
     *
     * Values are 0.0f - 1.0f
     * @param float r The red component of the color
     * @param float g The blue component of the color
     * @param float b The green component of the color
     * @param float a The alpha component of the color
     *
     */
    D3DCOLORVALUE arrColor = {fR, fG, fB, fA};
    m_Light.Diffuse = arrColor;
}

void CLight::setDiffuse(const D3DCOLORVALUE &arrColor) 
{
    /**
     * Sets the new diffuse color of the light
     *
     * @param D3DCOLORVALUE color The new diffuse color of the light
     *
     */
    m_Light.Diffuse = arrColor;
}

void CLight::getDiffuse(float &fR, float &fG, float &fB, float &fA) const 
{
    /**
     * Obtains the Diffuse color of the light.
     *
     * @param float &r A reference to the variable to hold the lights red
     *  diffuse component
     * @param float &g A reference to the variable to hold the lights green
     *  diffuse component
     * @param float &b A reference to the variable to hold the lights blue
     *  diffuse component
     * @param float &a A reference to the variable to hold the lights alpha
     *  diffuse component
     */
    fR = m_Light.Diffuse.r;
    fG = m_Light.Diffuse.g;
    fB = m_Light.Diffuse.b;
    fA = m_Light.Diffuse.a;
}

D3DCOLORVALUE CLight::getDiffuse() const 
{
    /* 
     * @return D3DCOLORVALUE The current color of the lights diffuse.
     */
    return m_Light.Diffuse;
}

void CLight::setSpecular(float fR, float fG, float fB, float fA) 
{
    /**
     * Creates and initialises a D3DCOLORVALUE using the parameters and sets 
     * it as the lights new specular.
     *
     * The specular color reacts with the materials specular color
     *
     * Values are 0.0f - 1.0f
     * @param float r The red component of the color
     * @param float g The blue component of the color
     * @param float b The green component of the color
     * @param float a The alpha component of the color
     *
     */
    D3DCOLORVALUE arrColor = {fR, fG, fB, fA};
    m_Light.Specular = arrColor;
}

void CLight::setSpecular(const D3DCOLORVALUE &arrColor) 
{
    /**
     * Sets the new specular color of the light
     *
     * @param D3DCOLORVALUE color The new specular color of the light
     *
     */
    m_Light.Specular = arrColor;
}

void CLight::getSpecular(float &fR, float &fG, float &fB, float &fA) const 
{
    /**
     * Obtains the Specular color of the light.
     *
     * @param float &r A reference to the variable to hold the lights red
     *  specular component
     * @param float &g A reference to the variable to hold the lights green
     *  specular component
     * @param float &b A reference to the variable to hold the lights blue
     *  specular component
     * @param float &a A reference to the variable to hold the lights alpha
     *  specular component
     */
    fR = m_Light.Specular.r;
    fG = m_Light.Specular.g;
    fB = m_Light.Specular.b;
    fA = m_Light.Specular.a;
}

D3DCOLORVALUE  CLight::getSpecular() const 
{
     /* 
     * @return D3DCOLORVALUE The current color of the lights Specular.
     */
    return m_Light.Specular;
}

void CLight::setAmbient(float fR, float fG, float fB, float fA) 
{
    /** 
     * Creates and initialises a D3DCOLORVALUE using the parameters and sets 
     * it as the lights new ambient.
     *
     * The ambient color reacts with the materials ambient color
     *
     * Values are 0.0f - 1.0f
     * @param float r The red component of the color
     * @param float g The blue component of the color
     * @param float b The green component of the color
     * @param float a The alpha component of the color
     *
     */
    D3DCOLORVALUE arrColor = {fR, fG, fB, fA};
    m_Light.Ambient = arrColor;
}

void CLight::setAmbient(const D3DCOLORVALUE &arrColor) 
{
    /**
     * Sets the new ambient color of the light
     *
     * @param D3DCOLORVALUE color The new ambient color of the light
     *
     */
    m_Light.Ambient = arrColor;
}

void CLight::getAmbient(float &fR, float &fG, float &fB, float &fA) const 
{
    /**
     * Obtains the Ambient color of the light.
     *
     * @param float &r A reference to the variable to hold the lights red
     * Ambient component
     * @param float &g A reference to the variable to hold the lights green
     * Ambient component
     * @param float &b A reference to the variable to hold the lights blue
     * Ambient component
     * @param float &a A reference to the variable to hold the lights alpha
     * Ambient component
     */
    fR = m_Light.Ambient.r;
    fG = m_Light.Ambient.g;
    fB = m_Light.Ambient.b;
    fA = m_Light.Ambient.a;
}

D3DCOLORVALUE CLight::getAmbient() const 
{
    /* 
     * @return D3DCOLORVALUE The current color of the lights ambiance.
     */
    return m_Light.Ambient;
}

void CLight::setRange(float fRange) 
{
    /**
     * Sets the new range of the light
     * 
     * A light's range property determines the distance, in world space, at 
     * which meshes in a scene no longer receive light emitted by that object.
     * Directional lights don't use the range property.
     *
     * @param float range The new range of the light
     */
    m_Light.Range = fRange;
}

float CLight::getRange() const
{
    /* 
     * @return float The current range of a spot light.
     */
    return m_Light.Range;
}

void CLight::setFalloff(float fFallOff) 
{
    /**
     * Sets the new falloff of the light
     * 
     * A value that determines the decrease in lighting between the inner and 
     * outer cones of a spotlight
     *
     * Most applications set Falloff to 1.0 to create falloff that occurs evenly
     * between the two cone
     *
     * @param float falloff The new range of the light
     *
     */
    m_Light.Falloff = fFallOff;
}

float CLight::getFalloff() const 
{
    /* 
     * @return float The current falloff for a spot light.
     */
    return m_Light.Falloff;
}

void CLight::setAttenuation(float fConstant, float fLinear, float fQuadratic) 
{
    /**
     * Sets the lights attenuation
     *
     * @param float constant
     * @param float linear
     * @param float quadratic
     *
     */
    m_Light.Attenuation0 = fConstant;
    m_Light.Attenuation1 = fLinear;
    m_Light.Attenuation2 = fQuadratic;
}

void CLight::getAttenuation(float &fConstant, float &fLinear, 
                            float &fQuadratic) const 
{
    /**
     * Obtains the values which determines how the light fades over time. The
     * falloff is determined by 3 values, a constant falloff, a liner falloff
     * and a quadratic falloff. The light intensity is between 0 and 1 and
     * determined as follows: Atten = 1/( const + linear * d + quadratic * d^2).
     *
     * The constant is the default intensity of the light.
     * The linear is a steady rate of fall off in respect to distance.
     * The quadratic is a compound fall off in respect to distance ^ 2.
     *
     * Most simple lights only activate one at a time; however, there are no
     * limitations to the falloff values and all three can be active at any
     * one time.
     *
     * @param &constant The constant falloff for the light
     * @param &liner The distance * value falloff for the light
     * @param &quadratic The distance^2 * value falloff for the light.
     */
    fConstant = m_Light.Attenuation0;
    fLinear = m_Light.Attenuation1;
    fQuadratic = m_Light.Attenuation2;
}

void CLight::setTheta(float fAngle) 
{
    /**
     * Sets the angle of the inner cone of the light of a spot light
     *
     * The Theta value is the radian angle of the spotlight's inner cone
     *
     * @param float angle The angle of the inner cone of a spot light
     *
     */
    m_Light.Theta = fAngle;
}

float CLight::getTheta() const 
{
    /* 
     * @return float The current theta angle of a spot light.
     */
    return m_Light.Theta;
}

void CLight::setPhi(float fAngle) 
{
    /**
     * Sets the angle of the outer cone of the light of a spot light
     *
     * The Phi value is the angle for the outer cone of light
     *
     * @param float angle The angle of the outer cone of a spot light
     *
     */
    m_Light.Phi = fAngle;
}

float CLight::getPhi() const
{
     /* 
     * @return float The current phi angle of a spot light.
     */
    return m_Light.Phi;
}

void CLight::setLight(int nIndex, LPDIRECT3DDEVICE9 pDevice)
{
    /**
     * Sets the current light to one of the 8 active lights
     *
     * @param int index The index in the list of active lights
     * @param LPDIRECT3DDEVICE9 pDevice The device the light is being set active
     *
     */

    pDevice -> SetLight(nIndex, &m_Light);
}